# React & Spring Boot ์—ฐ๋™




React์™€ Spring Boot์˜ ์—ฐ๋™์„ ์—ฐ์Šตํ•ด๋ณด์ž


Front-end : React

Back-end : Spring Boot


์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„ API ์—ญํ• ์„ ๊ตฌ์ถ•ํ•˜๊ณ , UI ๋กœ์ง์„ React์—์„œ ๋‹ด๋‹น ( React๋Š” ์ปดํฌ๋„ŒํŠธํ™”๊ฐ€ ์ž˜๋˜์–ด์žˆ์–ด์„œ ์žฌ์‚ฌ์šฉ์„ฑ์ด ์ข‹๊ณ , ์ˆ˜๋งŽ์€ ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉ ์žฅ์  ์กด์žฌ)


# ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๋„๊ตฌ (์„ค์น˜ํ•  ๊ฒƒ)

  • VSCode : ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ Java Extension Pack, Spring Boot Extension Pack ์„ค์น˜ (๋ฉ”๋‰ด-๊ธฐ๋ณธ์„ค์ •-์„ค์ •์—์„œ JDK ๊ฒ€์ƒ‰ ํ›„ 'setting.json์—์„œ ํŽธ์ง‘'์„ ๋“ค์–ด๊ฐ€ java.home์œผ๋กœ jdk ๊ฒฝ๋กœ ๋„ฃ์–ด์ฃผ๊ธฐ)
"java.home":  "C:\\Program Files\\Java\\jdk1.8.0_181" // ์ž์‹ ์˜ ๊ฒฝ๋กœ์— ๋งž์ถ”๊ธฐ
  • Node.js : 10.16.0

  • JDK(8 ์ด์ƒ)



# Spring Boot ์›น ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ


  1. VSCode์—์„œ ctrl-shift-p ์ž…๋ ฅ ํ›„, spring ๊ฒ€์ƒ‰ํ•ด์„œ Spring Initalizr: Generate Maven Project Spring ์„ ํƒ

  2. ํ”„๋กœ์ ํŠธ๋ฅผ ์„ ํƒํ•˜๋ฉด ๋‚˜์˜ค๋Š” ์งˆ๋ฌธ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž…๋ ฅ

    • ์–ธ์–ด : Java
    • Group Id : no4gift
    • Artifact Id : test
    • Spring boot version : 2.1.6
    • Dependency : DevTools, Spring Web Starter Web ๊ฒ€์ƒ‰ ํ›„ Selected

  3. ํ”„๋กœ์ ํŠธ๋ฅผ ์ €์žฅํ•  ํด๋”๋ฅผ ์ง€์ •ํ•˜๋ฉด Spring Boot ํ”„๋กœ์ ํŠธ๊ฐ€ ์„ค์น˜๋œ๋‹ค!


์ผ๋‹จ React๋ฅผ ๋ถ™์ด๊ธฐ ์ „์—, Spring Boot ์ž์ฒด๋กœ ์ž˜ ๊ตฌ๋™๋˜๋Š”์ง€ ์ง„ํ–‰ํ•ด๋ณด์ž

JSP์™€ JSTL์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. pom.xml์˜ dependencies ํƒœ๊ทธ ์•ˆ์— ์ถ”๊ฐ€ํ•˜์ž

<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
	<scope>provided</scope>
</dependency>

์ด์ œ ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•ด๋ณด์ž

VSCode์—์„œ ํ„ฐ๋ฏธ๋„ ์ฐฝ์„ ์—ด๊ณ  .\mvnw spring-boot:run์„ ์ž…๋ ฅํ•˜๋ฉด ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


๋งŒ์•ฝ ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด?

***************************
APPLICATION FAILED TO START
***************************
 
Description:
 
The Tomcat connector configured to listen on port 8080 failed to start. The port may already be in use or the connector may be misconfigured.

8080ํฌํŠธ๋ฅผ ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ด๋ผ ๊ตฌ๋™์ด ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค.

cmd์ฐฝ์„ ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์œผ๋กœ ์—ด๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ์ง„ํ–‰ํ•˜์ž


netstat -ao |find /i "listening"

ํ˜„์žฌ ๊ตฌ๋™ ์ค‘์ธ ํฌํŠธ๋“ค์ด ๋‚˜์˜จ๋‹ค. ์ด์ค‘์— 8080 ํฌํŠธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๊ฐ€์žฅ ์˜ค๋ฅธ์ชฝ์— ๋‚˜์˜ค๋Š” ์ˆซ์ž๊ฐ€ PID๋ฒˆํ˜ธ๋‹ค. ์ด๊ฑธ kill ํ•ด์ค˜์•ผ ํ•œ๋‹ค.


taskkill /f /im [pid๋ฒˆํ˜ธ]

๋‹ค์‹œ ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•ด๋ณด๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค!



# React ํ™˜๊ฒฝ ์ถ”๊ฐ€ํ•˜๊ธฐ


ํ„ฐ๋ฏธ๋„์„ ํ•˜๋‚˜ ๋” ์ถ”๊ฐ€๋กœ ์—ด๊ณ , npm init์„ ์ž…๋ ฅํ•ด pakage.json ํŒŒ์ผ์ด ์ƒ๊ธฐ๋„๋ก ํ•˜์ž

๋‚˜์˜ค๋Š” ์งˆ๋ฌธ๋“ค์€ ๋ชจ๋‘ enter ๋ˆ„๋ฅด๊ณ  ๋„˜์–ด๊ฐ€๋„ ๊ดœ์ฐฎ์Œ

์ด์ œ React ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ์˜์กด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•œ๋‹ค.


npm i react react-dom

npm i @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader style-loader webpack webpack-cli -D

create-react-app์œผ๋กœ ํ•œ๋ฒˆ์— ์„ค์น˜๋„ ๊ฐ€๋Šฅํ•จ


# webpack ์„ค์ •ํ•˜๊ธฐ

webpack์„ ํ†ตํ•ด react ๊ฐœ๋ฐœ ์‹œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋Šฅ๊ณผ jsp์— ํฌํ•จํ•  .js ํŒŒ์ผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๊ฒฝ๋กœ์— webpack.config.js ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ถ™์—ฌ๋„ฃ๊ธฐ


var path = require('path');
 
module.exports = {
    context: path.resolve(__dirname, 'src/main/jsx'),
    entry: {
        main: './MainPage.jsx',
        page1: './Page1Page.jsx'
    },
    devtool: 'sourcemaps',
    cache: true,
    output: {
        path: __dirname,
        filename: './src/main/webapp/js/react/[name].bundle.js'
    },
    mode: 'none',
    module: {
        rules: [ {
            test: /\.jsx?$/,
            exclude: /(node_modules)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: [ '@babel/preset-env', '@babel/preset-react' ]
                }
            }
        }, {
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]
        } ]
    }
};

  • ์ฝ”๋“œ ๋‚ด์šฉ

React ์†Œ์Šค ๊ฒฝ๋กœ๋ฅผ src/main/jsx๋กœ ์„ค์ •

MainPage์™€ Page1Page.jsx ๋นŒ๋“œ

๋นŒ๋“œ ๊ฒฐ๊ณผ js ํŒŒ์ผ๋“ค์„ src/main/webapp/js/react ์•„๋ž˜ [ํŽ˜์ด์ง€ ์ด๋ฆ„].bundle.js๋กœ ๋†“์Œ



# ์„œ๋ฒ„ ์ฝ”๋“œ ๊ฐœ๋ฐœํ•˜๊ธฐ


VSCode์—์„œ ํŒจํ‚ค์ง€ ์•ˆ์— MyController.java๋ผ๋Š” ํด๋ž˜์Šค ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค.


package no4gift.test;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
 
@Controller
public class MyController {
 
    @GetMapping("/{name}.html")
    public String page(@PathVariable String name, Model model) {
        model.addAttribute("pageName", name);
        return "page";
    }

}

์ถ”๊ฐ€๋กœ src/main์—๋‹ค๊ฐ€ webapp ํด๋”๋ฅผ ๋งŒ๋“ค์ž

webapp ํด๋” ์•ˆ์— jsp ํด๋”์™€ css ํด๋”๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.


๊ทธ๋ฆฌ๊ณ  jsp์™€ css ํŒŒ์ผ์„ ํ•˜๋‚˜์”ฉ ๋„ฃ์–ด๋ณด์ž

# src/main/webapp/jsp/page.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!doctype html>
<html>
<head>
    <title>${pageName}</title>
</head>
 
<body>
    <div id="root"></div>
    <script src="/js/react/${pageName}.bundle.js"></script>
</body>
</html>

# src/main/webapp/css/custom.css

.main { 
    font-size: 24px; border-bottom: solid 1px black; 
}
.page1 { 
    font-size: 14px; background-color: yellow; 
}


# ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ ๊ฐœ๋ฐœํ•˜๊ธฐ


์ด์ œ ์›นํŽ˜์ด์ง€์— ๋ณด์—ฌ์ค„ JSX ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด๋ณด์ž

src/main์— jsx ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  MainPage.jsx์™€ Page1Page.jsx 2๊ฐ€์ง€ jsx ํŒŒ์ผ์„ ๋งŒ๋“ค์—ˆ๋‹ค.

# src/main/jsx/MainPage.jsx

import '../webapp/css/custom.css';
 
import React from 'react';
import ReactDOM from 'react-dom';
 
class MainPage extends React.Component {
 
    render() {
        return <div className="main">no4gift ๋ฉ”์ธ ํŽ˜์ด์ง€</div>;
    }
 
}
 
ReactDOM.render(<MainPage/>, document.getElementById('root'));

# src/main/jsx/Page1Page.jsx

import '../webapp/css/custom.css';
 
import React from 'react';
import ReactDOM from 'react-dom';
 
class Page1Page extends React.Component {
 
    render() {
        return <div className="page1">no4gift์˜ Page1 ํŽ˜์ด์ง€</div>;
    }
 
}
 
ReactDOM.render(<Page1Page/>, document.getElementById('root'));

์•„๊นŒ ์ž‘์„ฑํ•œ cssํŒŒ์ผ์„ importํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, css ์ ์šฉ ๋ฐฉ์‹์€ ์ด๋ฐ–์—๋„ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.


์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํด๋ผ์ด์–ธํŠธ ํŽ˜์ด์ง€๋ฅผ ์„œ๋ฒ„ ๊ตฌ๋™ ํ›„ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๋นŒ๋“œ์‹œ์ผœ์•ผ ํ•œ๋‹ค!



# ํด๋ผ์ด์–ธํŠธ ์Šคํฌ๋ฆฝํŠธ ๋นŒ๋“œ์‹œํ‚ค๊ธฐ

jsx ํŒŒ์ผ์„ ์ˆ˜์ •ํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ง€์†์  ๋นŒ๋“œ๋ฅผ ์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ์ด ํ•„์š”ํ•˜๋‹ค.

์ด๋Š” webpack์˜ watch ๋ช…๋ น์„ ํ†ตํ•ด ๊ฐ€๋Šฅํ•˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

VSCode ํ„ฐ๋ฏธ๋„์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž…๋ ฅํ•˜์ž

node_modules\.bin\webpack --watch -d

-d๋Š” ๊ฐœ๋ฐœ์‹œ

-p๋Š” ์šด์˜์‹œ

ํ„ฐ๋ฏธ๋„ ํ™”๋ฉด์„ ๋ณด๋ฉด, webpack.config.js์—์„œ ์šฐ๋ฆฌ๊ฐ€ ์„ค์ •ํ•œ๋Œ€๋กœ ์ •์ƒ์ ์œผ๋กœ ๋นŒ๋“œ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.



src/main/webapp/js/react ์•„๋ž˜์— ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๋‘ ํŽ˜์ด์ง€์— ๋Œ€ํ•œ bundle.js ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์—ˆ์œผ๋ฉด ์ œ๋Œ€๋กœ ๋œ ๊ฒƒ์ด๋‹ค.


์„œ๋ฒ„ ๊ตฌ๋™์ด๋‚˜, ๋ฒˆ๋“ค๋ง์ด๋‚˜ ๋ช…๋ น์–ด ์ž…๋ ฅ์ด ์ƒ๋‹นํžˆ ๊ธธ๊ธฐ ๋•Œ๋ฌธ์— ๊ท€์ฐฎ๋‹คใ… ใ…  pakage.json์˜ script์— ๋“ฑ๋กํ•ด๋‘๋ฉด ๊ฐ„ํŽธํ•˜๊ฒŒ ๋นŒ๋“œ๊ณผ ์„œ๋ฒ„ ์‹คํ–‰์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "set JAVA_HOME=C:\\Program Files\\Java\\jdk1.8.0_181&&mvnw spring-boot:run",
    "watch": "node_modules\\.bin\\webpack --watch -d"
  },

์ด์ฒ˜๋Ÿผ start์™€ watch๋ฅผ ๋“ฑ๋กํ•ด๋‘๋Š” ๊ฒƒ!

start์˜ jdk๊ฒฝ๋กœ๋Š” ๊ฐ์ž ์ž์‹ ์˜ ๊ฒฝ๋กœ๋ฅผ ์ž…๋ ฅํ•ด์•ผํ•œ๋‹ค.

์ด์ œ ์šฐ๋ฆฌ๋Š” ๋นŒ๋“œ๋Š” npm run watch๋กœ, ์Šคํ”„๋ง ๋ถ€ํŠธ ์„œ๋ฒ„ ์‹คํ–‰์€ npm run start๋กœ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค~


๋นŒ๋“œ๊ฐ€ ์ด๋ฃจ์–ด์กŒ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํŽ˜์ด์ง€๋ฅผ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๋‹น ๊ฒฝ๋กœ๋กœ ๋“ค์–ด๊ฐ€๋ฉด ์šฐ๋ฆฌ๊ฐ€ jsxํŒŒ์ผ๋กœ ์ž‘์„ฑํ•œ ๋ชจ์Šต์ด ์ œ๋Œ€๋กœ ์ถœ๋ ฅ๋œ๋‹ค.


MainPage : http://localhost:8080/main.html


Page1Page : http://localhost:8080/page1.html


์—ฌ๊ธฐ๊นŒ์ง€ ์ง„ํ–‰ํ•œ ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ

์ด์™€ ๊ฐ™์€ ๊ณผ์ •์„ ํ† ๋Œ€๋กœ ๊ตฌํ˜„ํ•  ์›นํŽ˜์ด์ง€๋“ค์„ ์ƒ์„ฑํ•ด ๋‚˜๊ฐ€๋ฉด ๋œ๋‹ค.

์ด์ƒ React์™€ Spring Boot ์—ฐ๋™ํ•ด์„œ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ ๋!

์ตœ์ข… ์ˆ˜์ • : 12/17/2022, 7:23:59 AM